package com.kanshu;


import com.kanshu.constant.StatusType;
import com.kanshu.model.User;
import com.kanshu.service.impl.NovelServiceImpl;
import com.kanshu.service.impl.UserServiceImpl;
import com.kanshu.service.NovelService;
import com.kanshu.service.UserService;
import com.kanshu.utils.StringUtil;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;


/**
 * @author GAIst
 * ClassName SocketService.java
 * From
 * Description OK
 * createTime 2024年12月20日14:32:26
 */
public class SocketService {

    private static final Set<ObjectOutputStream> CLIENT_WRITERS = Collections.synchronizedSet(new HashSet<>());
    private static final Logger log = Logger.getLogger(SocketService.class);
    private static Map<String, User> users;
    private static NovelService novelService = new NovelServiceImpl();
    private static UserService userService = new UserServiceImpl();
    private static StatusType statusType = new StatusType();
    private static final Integer DEFAULT_PORT = 8880;
    private static final Integer CORE_POOL_SIZE = 5;
    private static final Integer MAXIMUM_POOL_SIZE = 10;
    private static final Long KEEP_ALIVE_TIME = 5000L;
    private static final TimeUnit UNIT = TimeUnit.MILLISECONDS;
    private static final String EXIT = "EXIT";

    /**
     * 主函数入口
     * 初始化服务器配置，创建服务器套接字并监听指定端口
     * 当有客户端连接时，创建一个新的线程来处理客户端请求
     *
     * @param args 命令行参数
     * @throws IOException 如果服务器套接字初始化失败或读取配置文件时发生错误
     */
    public static void main(String[] args) {
        // 加载 Log4j 配置文件
        try {
            String log4jConfigPath = "config/log4j.properties";
            System.out.println("Log4j configuration file path: " + SocketService.class.getClassLoader().getResource(log4jConfigPath));
            PropertyConfigurator.configure(SocketService.class.getClassLoader().getResource(log4jConfigPath).getPath());
        } catch (Exception e) {
            System.err.println("Failed to load Log4j configuration file: " + e.getMessage());
            e.printStackTrace();
            return;
        }

        int port;
        // 加载服务器配置属性
        Properties properties = new Properties();
        try (InputStream input = SocketService.class.getClassLoader().getResourceAsStream("config/server.properties")) {
            if (input == null) {
                throw new FileNotFoundException("server.properties 文件未找到");
            }
            properties.load(input);
            // 读取端口配置，如果没有配置，则使用默认端口
            port = Integer.parseInt(properties.getProperty("socket.server.port", String.valueOf(DEFAULT_PORT)));
        } catch (IOException ex) {
            System.err.println("配置文件读取失败: " + ex.getMessage());
            ex.printStackTrace();
            return;
        }


        // 任务队列
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);
        // 线程工厂
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        // 拒绝策略
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
        // 创建线程池
        ThreadPoolExecutor executorService = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAXIMUM_POOL_SIZE,
                KEEP_ALIVE_TIME,
                UNIT,
                workQueue,
                threadFactory,
                handler
        );

        // 创建服务器套接字
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("服务器已启动，监听端口: " + port);

            // 监听客户端连接请求
            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("新客户端连接: " + clientSocket.getInetAddress());

                // 为每个客户端连接分配一个线程进行处理
                executorService.execute(new ClientHandler(clientSocket));
            }
        } catch (BindException be) {
            System.err.println("端口 " + port + " 已被占用，请检查配置或选择其他端口");
            log.error("exception message", be);
        } catch (IOException ex) {
            System.err.println("服务器套接字初始化失败: " + ex.getMessage());
            log.error("exception message", ex);
        } finally {
            // 关闭线程池
            if (executorService != null) {
                executorService.shutdown();
            }
        }
    }


    private static class ClientHandler implements Runnable {
        private Socket socket;
        private ObjectInputStream in;
        private ObjectOutputStream out;

        public ClientHandler(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            Object request;
            // 初始化状态类型实例
            StatusType status = new StatusType();
            try {
                // 初始化输入流和输出流
                in = new ObjectInputStream(socket.getInputStream());
                out = new ObjectOutputStream(socket.getOutputStream());

                // 同步代码块，确保线程安全地添加输出流到客户端写入者集合
                synchronized (CLIENT_WRITERS) {
                    CLIENT_WRITERS.add(out);
                }

                // 循环读取客户端请求并处理
                while ((request = in.readObject()) != null) {
                    // 处理退出请求
                    if (EXIT.equals(request)) {
                        break;
                    }
                    // 处理用户类型的请求
                    if (request instanceof User) {
                        User user = (User) request;
                        // 根据用户操作类型处理登录或注册
                        if (statusType.getLOGIN().equals(user.getAction())) {
                            userService.handleLogin(user, users, out);
                        } else if (statusType.getENROLL().equals(user.getAction())) {
                            userService.handleEnroll(user, users, out);
                        } else {
                            // 处理未知请求
                            try {
                                out.writeObject(status.getUNKONWN());
                            } catch (IOException e) {
                                log.info("exception message", e);
                            }
                        }
                        // 处理字符串类型的请求
                    } else if (request instanceof String) {
                        // 根据请求类型执行相应的操作
                        if (request.equals(statusType.getSELECTMENU())) {
                            novelService.sendNovelTypeList(out);
                        } else if (request.equals(statusType.getENWUXIA())) {
                            novelService.sendNovelList(request, out);
                        } else if (request.equals(statusType.getENYANQING())) {
                            novelService.sendNovelList(request, out);
                        } else if (StringUtil.hasReadAll(request.toString())) {
                            novelService.readNovelAll(request, out);
                        } else if (StringUtil.hasReadFristPage(request.toString())) {
                            novelService.readNovelAll(request, out);
                        } else if (StringUtil.hasRead(request.toString())) {
                            novelService.readNovel(request, out);
                        } else if (StringUtil.hasDownload(request.toString())) {
                            novelService.downloadNovel(request, out);
                        } else if (StringUtil.hasUploadNovel(request.toString())) {
                            novelService.uploadNovel(request, out);
                        }
                    }
                }
            } catch (SocketException e) {
                log.info("客户端连接关闭: " + e.getMessage());
            } catch (IOException | ClassNotFoundException e) {
                log.error("exeception message", e);
            } finally {
                // 确保输出流被正确移除并关闭套接字
                // 确保在任何情况下都关闭资源
                try {
                    if (in != null) {
                        in.close();
                    }
                    if (out != null) {
                        out.close();
                    }
                    if (socket != null && !socket.isClosed()) {
                        socket.close();
                    }
                } catch (IOException e) {
                    System.err.println("Error closing resources: " + e.getMessage());
                    //okok
                }
            }
        }

    }
}

